package maps

import (
	"database/sql"
	"errors"
	"log"
	"reflect"
	"strings"
)

/*
  @Description:    列和属性映射关系
*/
type ColumnPropMap struct {
	IsId     bool
	ColName  string
	PropName string
}

/*
  结果集映射信息
*/
type ResultMap struct {
	//id
	Id string
	//对应的struct类型
	StructType reflect.Type
	//结果集列与struct成员的映射
	ColPropMaps []ColumnPropMap
}

/*
   获取某个struct的映射信息,提取struct的成员的标签中的column值
   @param structType 结构体的类型
   @param resultMapId resultMap的ID
   @return *ResultMap 映射信息
   @return error 错误信息
*/
func GetResultMap(structType reflect.Type, resultMapId string) (*ResultMap, error) {
	if nil == structType {
		return nil, errors.New("structType is nil")
	}

	if "" == strings.TrimSpace(resultMapId) {
		return nil, errors.New("resultMapId is nil")
	}

	if structType.Kind() != reflect.Struct {
		return nil, errors.New("structType must be reflect.Struct")
	}

	numField := structType.NumField()
	columnPropMaps := make([]ColumnPropMap, numField)
	resultMap := ResultMap{
		Id:          resultMapId,
		StructType:  structType,
		ColPropMaps: columnPropMaps,
	}

	for i := 0; i < numField; i++ {
		columnPropMaps[i] = ColumnPropMap{
			PropName: structType.Field(i).Name,
			ColName:  structType.Field(i).Tag.Get("column"),
		}

		if "true" == structType.Field(i).Tag.Get("id") {
			columnPropMaps[i].IsId = true
		} else {
			columnPropMaps[i].IsId = false
		}

	}

	if 0 == len(columnPropMaps) {
		return nil, errors.New("no column tag value")
	}

	return &resultMap, nil

}

/*
   返回逗号分隔的列列表

   @param *ResultMap 映射信息
   @param tableAlia 表的别名
   @return string 列列表
   @return error 错误信息
*/
func GetColumns(resultMap *ResultMap, tableAlia string) (string, error) {
	if nil == resultMap {
		return "", errors.New("resultMap is nil")
	}

	colCount := len(resultMap.ColPropMaps)
	if 0 == colCount {
		return "", errors.New("no columns")
	}

	colNames := make([]string, colCount)
	for index, colPropMap := range resultMap.ColPropMaps {
		colNames[index] = colPropMap.ColName + " " + tableAlia
	}

	return strings.Join(colNames, ","), nil

}

/*
   将查询结果集中数据装配成struct,会关闭Rows

   @param rows 数据库查询结果集
   @param resultMap 查询结果集对应的映射信息
   @return datas 装配好的数据
   @return err 错误信息
*/
func GetStructFromResult(rows *sql.Rows, resultMap *ResultMap) (datas []interface{}, err error) {
	if nil == rows {
		return nil, errors.New("rows is nil")
	}

	if nil == resultMap {
		return nil, errors.New("resultMap is nil")
	}

	defer rows.Close()

	log.Println(rows.ColumnTypes())

	colTypes, err := rows.ColumnTypes()
	if nil != err {
		return nil, err
	}

	datas = make([]interface{},10)

	//TODO
	for rows.Next() {
		data := reflect.New(resultMap.StructType)
		v := reflect.ValueOf(data)

		//获取struct 对应的成员地址
		colCount := len(colTypes)
		rowData := make([]*interface{}, colCount)
		for i, ct := range colTypes {

			field := v.FieldByName(ct.Name())
			//没有此列映射关系
			if !field.IsValid() {
				continue
			}

			o := field.Interface()
			rowData[i] = &o
		}

		rows.Scan(rowData)

		_ = append(datas, data.Interface())

	}

	return datas, nil
}
